package com.bgi.cg.oss4j.provider.tencent.service;

import com.bgi.cg.api.AbstractOssService;
import com.bgi.cg.api.entity.OssResponse;
import com.bgi.cg.oss4j.common.util.OssUtil;
import com.bgi.cg.oss4j.provider._base.OssSupplierType;
import com.bgi.cg.oss4j.provider.tencent.config.TencentOssConfig;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.BasicSessionCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.model.Bucket;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.region.Region;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Response;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import java.util.function.Function;

/**
 * @Description 腾讯云
 * @Author Z.cg
 * @Date 2023/9/8
 * @Version 1.0
 **/
@Slf4j
public class TencentOssService extends AbstractOssService {
    private final TencentOssConfig config;
    private COSClient client;

    public TencentOssService(TencentOssConfig tencentOssConfig, Executor pool) {
        super(pool);
        this.config = tencentOssConfig;
        COSCredentials cred = new BasicCOSCredentials(tencentOssConfig.getAccessKey(), tencentOssConfig.getAccessKeySecret());
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.setRegion(new Region(tencentOssConfig.getRegion()));
        client = new COSClient(cred, clientConfig);
        if (tencentOssConfig.isBucketAutoCreate()) {
            try {
                if (!doesBucketExist(tencentOssConfig.getBucket())) {
                    createBucket(tencentOssConfig.getBucket());
                }
            } catch (Exception exception) {
                log.error("【{}】COS桶创建失败：{}", OssSupplierType.TENCENT.name(), exception.getMessage());
            }
        }
    }

    @Override
    public OssResponse upload(File file) {
        return upload(file, config.getBucket());
    }

    @Override
    public OssResponse upload(File file, String bucketName) {
        return upload(file, bucketName, OssUtil::generatePath);
    }

    @Override
    public OssResponse upload(File file, Function<File, String> fileKeyFunction) {
        return upload(file, config.getBucket(), fileKeyFunction);
    }

    @Override
    public OssResponse upload(File file, String bucketName, Function<File, String> fileKeyFunction) {
        valiConfigIsOk();
        // 生成存储的文件名即存储的路径
        String fileName = fileKeyFunction.apply(file);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, file);
        OssResponse ossResponse = OssResponse.builder().code("0").filePath(fileName).ossType(OssSupplierType.TENCENT.name()).build();
        // 设置存储类型（如有需要，不需要请忽略此行代码）, 默认是标准(Standard), 低频(standard_ia)
        // 更多存储类型请参见 https://cloud.tencent.com/document/product/436/33417
        //putObjectRequest.setStorageClass(StorageClass.Standard_IA);
        try {
            getClient().putObject(putObjectRequest);
        } catch (Exception exception) {
            log.error("【{}】COS文件上传失败：{}", OssSupplierType.TENCENT.name(), exception.getMessage());
            ossResponse.setFail(exception.getMessage());
        }
        return ossResponse;
    }

    @Override
    public OssResponse genPreviewUrl(String fileName) {
        return genPreviewUrl(fileName, config.getBucket());
    }

    @Override
    public OssResponse genPreviewUrl(String fileName, String bucketName) {
        OssResponse ossResponse = OssResponse.builder().code("0").build();
        // 存储桶的命名格式为 BucketName-APPID，此处填写的存储桶名称必须为此格式
        URL url = getClient().getObjectUrl(bucketName, fileName);
        ossResponse.setPreviewUrl(url.toString());
        return ossResponse;
    }

    @Override
    public OssResponse delete(String fileName) {
        return delete(fileName, config.getBucket());
    }

    @Override
    public OssResponse delete(String fileName, String bucketName) {
        OssResponse ossResponse = OssResponse.builder().code("0").build();
        try {
            getClient().deleteObject(bucketName, fileName);
        } catch (Exception exception) {
            log.error("【{}】COS文件删除失败：{}", OssSupplierType.TENCENT.name(), exception.getMessage());
            ossResponse.setFail(exception.getMessage());
        }
        return ossResponse;
    }

    COSClient getClient() {
        Sts sts = getTmpSTS();
        COSCredentials cred = new BasicSessionCredentials(sts.getTmpSecretId(), sts.getTmpSecretKey(), sts.getSessionToken());
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.setRegion(new Region(config.getRegion()));
        // 设置请求协议, http 或者 https
        // 5.6.53 及更低的版本，建议设置使用 https 协议
        // 5.6.54 及更高版本，默认使用了 https
        //clientConfig.setHttpProtocol(HttpProtocol.https);
        // 以下的设置，是可选的：
        // 设置 socket 读取超时，默认 30s
        clientConfig.setSocketTimeout(30 * 1000);
        // 设置建立连接超时，默认 30s
        clientConfig.setConnectionTimeout(30 * 1000);
        // 如果需要的话，设置 http 代理，ip 以及 port
        //clientConfig.setHttpProxyIp("httpProxyIp");
        clientConfig.setHttpProxyPort(80);
        // 生成 cos 客户端。
        return new COSClient(cred, clientConfig);
    }


    boolean doesBucketExist(String bucketName) {
        return client.doesBucketExist(bucketName);
    }

    Bucket createBucket(String bucketName) {
        return client.createBucket(bucketName);
    }

    /**
     * 临时秘钥
     */
    @Data
    class Sts {
        String tmpSecretId;
        String tmpSecretKey;
        String sessionToken;
    }

    Sts getTmpSTS() {
        TreeMap<String, Object> c = new TreeMap<String, Object>();
        c.put("secretId", config.getAccessKey());
        // 替换为您的云 api 密钥 SecretKey
        c.put("secretKey", config.getAccessKeySecret());
        // 临时密钥有效时长，单位是秒，默认 1800 秒，目前主账号最长 2 小时（即 7200 秒），子账号最长 36 小时（即 129600）秒
        c.put("durationSeconds", 1800);
        // 换成您的 bucket
        c.put("bucket", config.getBucket());
        // 换成 bucket 所在地区
        c.put("region", config.getRegion());
        String allowPreFixes = config.getAllowPreFixes();
        c.put("allowPrefixes", allowPreFixes.split(","));
        String allowActions = config.getAllowActions();
        c.put("allowActions", allowActions.split(","));
        Sts sts = new Sts();
        try {
            Response response = CosStsClient.getCredential(c);
            sts.setTmpSecretId(response.credentials.tmpSecretId);
            sts.setTmpSecretKey(response.credentials.tmpSecretKey);
            sts.setSessionToken(response.credentials.sessionToken);
        } catch (IOException exception) {
            log.error("【{}】COS临时秘钥获取失败：{}", OssSupplierType.TENCENT.name(), exception.getMessage());
        }
        return sts;
    }

    void valiConfigIsOk() {
        if (!config.configIsOk()) {
            throw new RuntimeException("未配置[accessKey,accessKeySecret,bucket,endPoint,region]值");
        }
    }
}
